﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using Spring.Objects.Factory.Support;
using Strongshell.Recoil.Core.Composition.Parsing.DependencyInjection;

namespace Strongshell.Recoil.Core.Composition.Parsing.Handlers
{
    public class ListInitExpressionHandler : IExpressionHandler
    {
        private readonly IExpressionParser expressionParser;
        private readonly IExpressionHandler compilingHandler;
        private readonly IObjectConfigurationRegistry objectConfigurationRegistry;

        public ListInitExpressionHandler(IObjectConfigurationRegistry objectConfigurationRegistry, IExpressionParser expressionParser, IExpressionHandler compilingHandler)
        {
            this.expressionParser = expressionParser;
            this.objectConfigurationRegistry = objectConfigurationRegistry;
            this.compilingHandler = compilingHandler;
        }

        public void Handle(IDependencyInjector currentInjector, Expression currentExpression)
        {
            var listInitExpression = (ListInitExpression)currentExpression;

            // We can convert the list to a ManagedList if the list implements IList and all
            // Add() calls use only one argument
            if (typeof(IList).IsAssignableFrom(listInitExpression.Type)
                && listInitExpression.Initializers.All(e => e.Arguments.Count == 1))
            {
                var managedList = new ManagedList();
                currentInjector.Inject(managedList);

                if (listInitExpression.Type.IsGenericType)
                {
                    managedList.ElementTypeName = listInitExpression.Type.GetGenericArguments()[0].AssemblyQualifiedName;
                }

                var currentConfiguration = currentInjector.SelectObjectConfiguration();

                foreach (var initializer in listInitExpression.Initializers)
                {
                    expressionParser.Parse(new ListDependencyInjector(objectConfigurationRegistry,
                                                                      currentConfiguration,
                                                                      initializer.AddMethod.GetParameters().First().
                                                                          ParameterType,
                                                                      managedList),
                                           initializer.Arguments.First());
                }
            }
            else if (typeof(IDictionary).IsAssignableFrom(listInitExpression.Type)
                && listInitExpression.Initializers.All(e => e.Arguments.Count == 2))
            {
                var managedDictionary = new ManagedDictionary();
                currentInjector.Inject(managedDictionary);

                if(listInitExpression.Type.IsGenericType)
                {
                    var genericArguments = listInitExpression.Type.GetGenericArguments();
                    if (genericArguments.Length == 2)
                    {
                        managedDictionary.KeyTypeName = genericArguments[0].AssemblyQualifiedName;
                        managedDictionary.ValueTypeName = genericArguments[1].AssemblyQualifiedName;
                    }
                }

                var currentConfiguration = currentInjector.SelectObjectConfiguration();

                foreach (var initializer in listInitExpression.Initializers)
                {
                    var context = new DictionaryDependencyInjector(objectConfigurationRegistry,
                                                                   currentConfiguration,
                                                                   initializer.AddMethod.GetParameters()[0].ParameterType,
                                                                   managedDictionary);

                    expressionParser.Parse(context, initializer.Arguments[0]);

                    context.ExpectedType = initializer.AddMethod.GetParameters()[1].ParameterType;

                    expressionParser.Parse(context, initializer.Arguments[1]);
                }
            }
            else
            {
                compilingHandler.Handle(currentInjector, currentExpression);
            }
        }
    }
}
